﻿#include "libcomm.h"

static char _BASE64CHAR[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

inline char __GetBase64Char(int index)
{
	MYLIB_ASSERT(index >= 0 && index < 64);
	return _BASE64CHAR[index];
}

inline char __GetCharIndex(char c) 
{
	if ((c >= 'A') && (c <= 'Z'))
	{
		return c - 'A';
	}
	else if ((c >= 'a') && (c <= 'z'))
	{
		return c - 'a' + 26;
	}
	else if ((c >= '0') && (c <= '9'))
	{
		return c - '0' + 52;
	}
	else if (c == '+')
	{
		return 62;
	}
	else if (c == '/')
	{
		return 63;
	}
	else if (c == '=')
	{
		return 0;
	}
	return 64; // illegal char
}

String CodecBase64::Encode(const void* buffer, int size)
{
	String base64((size + 2) / 3 * 4 + 1);
	unsigned char* dst = (unsigned char*)base64.get();
	unsigned char* src = (unsigned char*)buffer;
	int src_len = size;
	int dest_len = 0;

	while (src_len > 0 && NULL != src)  
	{
		*dst++ = __GetBase64Char((src[0] >> 2) & 0x3F);  
		if (src_len > 2) 
		{
			*dst++ = __GetBase64Char(((src[0] & 3) << 4) | (src[1] >> 4));
			*dst++ = __GetBase64Char(((src[1] & 0xF) << 2) | (src[2] >> 6));
			*dst++ = __GetBase64Char(src[2] & 0x3F);
		}
		else
		{
			switch (src_len) 
			{
			case 1:
				*dst++ = __GetBase64Char((src[0] & 3) << 4);
				*dst++ = '=';
				*dst++ = '=';
				break;
			case 2:
				*dst++ = __GetBase64Char(((src[0] & 3) << 4) | (src[1] >> 4));
				*dst++ = __GetBase64Char(((src[1] & 0x0F) << 2) | (src[2] >> 6));
				*dst++ = '=';
				break;
			}
		}
		src += 3;
		src_len -= 3;
		dest_len += 4;
	}
	*dst = 0;
	MYLIB_ASSERT(dest_len < base64.size());
	return base64;
}

String CodecBase64::Encode(const Buffer& buffer)
{
	return Encode(buffer.get(),buffer.size());
}


Buffer CodecBase64::Decode(const char* base64, int len)
{
	if (0 == len && 0 != base64)
		len = strlen(base64);
	if (0 == base64 || 0 == len || len % 4)   
		return Buffer();

	Buffer buffer(len / 4 * 3);
	unsigned char* dst = (unsigned char*)buffer.get();
	int dest_len = 0;
	char code[4];
	while (len > 3) 
	{
		for (int i=0;i<4;i++)
		{
			code[i] = __GetCharIndex(base64[i]);
			if (code[i] > 63)
			{
				LOGERR("%s: illegal base64 character.", __FUNCTION__);
				return Buffer();
			}
		}

		*dst++ = (code[0] << 2) | (code[1] >> 4);
		*dst++ = (code[1] << 4) | (code[2] >> 2);
		*dst++ = (code[2] << 6) | (code[3]);

		len -= 4;
		dest_len += 3;
		if ('=' == base64[2])
		{
			dest_len -= 2;
			break;
		}
		if ('=' == base64[3])
		{
			dest_len--;
			break;
		}

		base64 += 4;
	}
	MYLIB_ASSERT(dest_len <= buffer.size());
	buffer.resize(dest_len, false);
	return buffer;
}